package data.base;

public class Vector3{
  private double[] coords = new double[3];
  
  public static final int _X = 0;
  public static final int _Y = 1;
  public static final int _Z = 2;
  
  /**
   * Simply creates a vector for the given coordinates
   * @param x
   * @param y
   * @param z
   */
  public Vector3(double x, double y, double z) {
    coords[_X] = x;
    coords[_Y] = y;
    coords[_Z] = z;
  }
  
  /**
   * Constructs a random normal
   * @param inaccurate
   */
  public Vector3(boolean inaccurate){
    if(inaccurate){
      coords[_X] = Math.random() - 0.5;
      coords[_Y] = Math.random() - 0.5;
      coords[_Z] = Math.random() - 0.5;
    }
    else{
      double xy = Math.random();
      double xz = Math.random();
      double yz = Math.random();
      
      coords[_X] = Math.sin(xy * 2 * Math.PI);
      coords[_Y] = Math.cos(xy * 2 * Math.PI);
      
      coords[_X] += Math.sin(xz * 2 * Math.PI);
      coords[_Z] = Math.cos(xz * 2 * Math.PI);
      
      coords[_Y] += Math.sin(yz * 2 * Math.PI);
      coords[_Z] += Math.cos(yz * 2 * Math.PI);
    }
    normalize();
  }
  
  public Vector3(double[] xyz){
    coords = xyz;
  }
  
  /**
   * Creates the cross product of the given vectors. The resulting
   * vector is the normal of the plane spanning between the given vectors.
   * @param v1
   * @param v2
   * @param normalize
   */
  public Vector3(Vector3 v1, Vector3 v2, boolean normalize){
    this(
        v1.getY() * v2.getZ() - v1.getZ() * v2.getY(), 
        v1.getZ() * v2.getX() - v1.getX() * v2.getZ(), 
        v1.getX() * v2.getY() - v1.getY() * v2.getX());
    if(normalize)
      normalize();
  }
  
  public Vector3(Vector3 v1, Vector3 v2){
    this(v2.getX(), v2.getY(), v2.getZ());
    subtract(v1);
  }
  
  public void normalize(){
    multiplicate(1 / getMagnitude());
  }
  
  public Vector3 reflect(Vector3 normal){
    double sp = getDotProduct(normal) * 2;
    coords[_X] -= normal.getX() * sp;
    coords[_Y] -= normal.getY() * sp;
    coords[_Z] -= normal.getZ() * sp;
    return this;
  }
  
  public void multiplicate(double factor){
    coords[_X] *= factor;
    coords[_Y] *= factor;
    coords[_Z] *= factor;
  }
  
  public void add(Vector3 p){
    coords[_X] += p.getX();
    coords[_Y] += p.getY();
    coords[_Z] += p.getZ();
  }
  
  public void add(Vector3 p, double factor){
    coords[_X] += p.getX() * factor;
    coords[_Y] += p.getY() * factor;
    coords[_Z] += p.getZ() * factor;
  }
  
  public void subtract(Vector3 p){
    coords[_X] -= p.getX();
    coords[_Y] -= p.getY();
    coords[_Z] -= p.getZ();
  }
  
  public double getMagnitude(){
    return Math.sqrt(coords[_X]*coords[_X] + coords[_Y]*coords[_Y] + coords[_Z]*coords[_Z]);
  }
  
  public void setMagnitude(double magnitude){
    multiplicate(magnitude / getMagnitude());
  }
  
  public double getDotProduct(Vector3 v){
    return v.getX() * coords[_X] + v.getY() * coords[_Y] + v.getZ() * coords[_Z];
  }
  
  @Override
  public Vector3 clone(){
    return new Vector3(coords[_X], coords[_Y], coords[_Z]);
  }
  
  public double distanceTo(Vector3 p){
    double a = getX() - p.getX();
    double b = getY() - p.getY();
    double c = getZ() - p.getZ();
    return Math.sqrt(a*a + b*b + c*c);
  }
  
  public double getAngleBetween(Vector3 a, Vector3 b){
    double abDist = a.distanceTo(b);
    double aDist = distanceTo(a);
    double bDist = distanceTo(b);
    return Math.acos((aDist * aDist + bDist * bDist - abDist * abDist) / (2 * aDist * bDist));
  }
  
  public double getX() {
    return coords[_X];
  }

  public void setX(double x) {
    coords[_X] = x;
  }

  public double getY() {
    return coords[_Y];
  }

  public void setY(double y) {
    coords[_Y] = y;
  }

  public double getZ() {
    return coords[_Z];
  }

  public void setZ(double z) {
    coords[_Z] = z;
  }
  
  public double get(int axis){
    return coords[axis];
  }
  
  public void set(double x, double y, double z){
    coords[_X] = x;
    coords[_Y] = y;
    coords[_Z] = z;
  }
  
  public void copy(Vector3 v){
    coords[_X] = v.getX();
    coords[_Y] = v.getY();
    coords[_Z] = v.getZ();
  }
  
  @Override
  public String toString(){
    return "(" + getX() + "/" + getY() + "/" + getZ() + ")";
  }
  

  @Override
  public int hashCode(){
    return (int)(getX() / getY() * getZ());
  }
  
  @Override
  public boolean equals(Object v){
    return ((Vector3)v).getX() == getX() && ((Vector3)v).getY() == getY() && ((Vector3)v).getZ() == getZ();
  }
}
